/*
 *  Autores: Claudio Djohnnatha, David Guilherme, Jailson Lima e Jordan Lira.
 *
 *  Projeto da disciplina Linguagem de Programação 2.
 *
 *  Arquivo: ProtocolComunication.java
 *
 *  Descrição: O ProtocolComunication serve para gerenciar a sincronização de todos os ClientApplication
 *  conectados ao Servidor ( Server ) atráves do ProtocolServer de cada um deles.
 *
 *  Data: 27/08/2013
 *
 */



import java.util.LinkedList;

public class ProtocolComunication
{
   private Document document;
   private String title;
   private StringBuilder text;

   // Referência para o ProtocolServer associado ao ClientApplication que está atualmente escrevendo.
   private ProtocolServer current_protocolServer;
   
   // Lista de tarefas gerenciadas pelo objeto Executor.
   private LinkedList< ProtocolServer > list;
   
   // Buffer de posições
   private LinkedList< Integer > bufferPositions;

   public ProtocolComunication( Document d )
   {
      document = d;
      title = document.getTitle();
      text = new StringBuilder( document.getText() );
      list = new LinkedList< ProtocolServer >();
      bufferPositions = new LinkedList< Integer >();
   }

   public synchronized void processLine( String fromClient, ProtocolServer protocolServer )
   {
      // Atualiza a referência para o ProtocolServer atual.
      current_protocolServer = protocolServer;

      String[] tokens = fromClient.split( " " );

      // tokens[ 0 ] => DELETE/BACKSPACE/SPACE/ENTER/TAB/CHARACTER
      // tokens[ 1 ] => position
      // tokens[ 2 ] => length
      // tokens[ 3 ] => W [ se a operação não for CHARECTER ]
      // tokens[ 3 ] => char [ se a operação for CHARECTER ]
      // tokens[ 4 ] => W [ se a operação for CHARECTER ]

      int position = Integer.parseInt( tokens[ 1 ] ); // Posição de inserção de caractere no texto.
      int lengthText = Integer.parseInt( tokens[ 2 ] ); // Tamanho do texto do cliente que está digitando

      if( text.length() == lengthText && bufferPositions.size() != 0 )
      {  
         bufferPositions.clear();
         bufferPositions.add( position );
      }else
      {
         int addPosition = position;

         for( int i : bufferPositions )
            if( i < position )
            {   
               position += text.length() - lengthText; 
               break;
            }

         bufferPositions.add( addPosition );

      }

      switch( tokens[ 0 ].charAt( 0 ) )
      {
         case 'C': // CHARACTER
           
            if ( position < 0 || position > text.length() ) return;

            text.insert( position, tokens[ 3 ] );

            updateAll( text.toString() + " " + tokens[ 0 ] + " " + position );
         
         break;
         case 'S': // SPACE

            if ( position < 0 || position > text.length() ) return;

            text.insert( position, " " );

            updateAll( text.toString() + " " + tokens[ 0 ] + " " + position );
         
         break;
         case 'E': // ENTER
         
            if ( position < 0 || position > text.length() ) return;

            text.insert( position, "\n" );

            updateAll( text.toString() + " " + tokens[ 0 ] + " " + position );
         
         break;
         case 'T': // TAB

            if ( position < 0 || position > text.length() ) return;

            text.insert( position, "\t" );

            updateAll( text.toString() + " " + tokens[ 0 ] + " " + position );

         break;
         case 'D': // DELETE

            if ( position < 0 || position > text.length() - 1 ) return;

            text.deleteCharAt( position );
            updateAll( text.toString() + " " + tokens[ 0 ] + " " + position );

         break;
         case 'B': // BACKSPACE

            if ( position < 1 || position > text.length() ) return;

            text.deleteCharAt( position - 1 );
            updateAll( text.toString() + " " + tokens[ 0 ] + " " + position );

         break;
      }

   }

   public Document getDocument()
   {
      document.setText( text.toString() );
      return document;
   }

   // Retorna o título do documento.
   public String getTitle()
   {
      return title;
   }

   // Retorna o texto sincrônizado com todos os ClientApplication.
   public String getText()
   {
      return text.toString();
   }

   public synchronized void renameTitle( String new_title )
   {
      title = new_title;
      document.setTitle( title );

      // Atualiza o título para todos os clientes que estejam editando este documento.
      for ( int i = 0; i < list.size(); i++ )
         list.get( i ).renameTitle( title );
   }

   public int sizeListProtocolServer()
   {
      return list.size();
   }

   // Método para adicionar um novo ProtocolServer criando a list.
   public synchronized void addListServer( ProtocolServer protocolServer )
   {
      list.add( protocolServer );
   }

   // Método para remover um ProtocolServer da list.
   public synchronized void removeListServer( ProtocolServer protocolServer )
   {
      list.remove( protocolServer );
   }

   // Método utilizado para sincronizar todos os ClientApplication associados a cada ProtocolServer.
   // Esse método faz com que cada ProtocolServer escreva na sua "porta" out, para que o seu
   // ClientApplication associado receba a atualização na sua respectiva "portas" in.
   public void updateAll( String synchronized_text )
   {
      for ( ProtocolServer p : list )
         if ( p == current_protocolServer )
            p.update( synchronized_text + " MYSELF" );
         else
            p.update( synchronized_text + " OTHER" );
   }

   // Atualização enviada do servidor se algum documento for renomeado ou excluído.
   public void updateServer( String message_update )
   {
      for ( ProtocolServer p : list)
         p.updateServer( message_update );
   }

}